/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：呼叫中心-请求处理层-权限控制管理
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taiping.cc.authorize.webapi.handler;

import com.taiping.dlt.pojo.bo.AuthLoginBO;
import com.taiping.dlt.pojo.query.AuthLoginQO;
import com.taipingframework.boot.cache.constant.DurationConstant;
import com.taipingframework.boot.cache.redis.StandardCacheKeyBuilder;
import com.taipingframework.boot.web.client.BrowserCookie;
import com.taipingframework.boot.web.client.ServletCookieScene;
import com.taipingframework.boot.web.response.result.ApiResult;
import com.taipingframework.boot.web.response.status.ApiStatusEnum;
import com.taipingframework.utility.exception.ApplicationException;
import com.taipingframework.utility.exception.SystemMessageException;
import com.taipingframework.utility.thread.wrapper.BenchmarksFuture;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.function.Function;

/**
 * 登录认证web-api请求转发
 */
@Component
@RequiredArgsConstructor
public class SecurityHandler {

    private final AccountHandler accountHandler;
    private final SessionHandler sessionHandler;

    /**
     * 判断当前会话是否处于登录状态
     */
    public BenchmarksFuture<ApiResult<Boolean>> check() {
        return sessionHandler.loginCheck(
        ).thenApplyAsync(flag -> ApiResult.<Boolean>build()
                .setStatus(ApiStatusEnum.SUCCESS)
                .setResult(flag)
        ).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

    /**
     * 本地登录
     */
    public BenchmarksFuture<ApiResult<String>> login(AuthLoginQO loginQO) {
        //TODO - 在企业统一认证中心登录之后，将会携带票据ticket跳转到当前系统的前端页面，前端页面调用后端登录接口时会将票据ticket进一步传递。
        // 此处拿到票据之后，依据该参数去企业统一认证中心识别登录人的唯一凭证
        // 然后，依据该凭证创建本地会话

        Function<AuthLoginQO, BenchmarksFuture<String>> loginAction = (authLoginQO) -> {
            // 直接调用登录接口，如果是重复的登录操作，每次都将执行会话验证逻辑与数据查询逻辑，影响登录接口的性能。
            return accountHandler.valid(authLoginQO
            ).handle((apiResult, throwable) -> {
                if (throwable != null) {
                    throw new ApplicationException(throwable.getCause());
                }
                if (!apiResult.getSuccess()) {
                    throw new SystemMessageException(apiResult);
                }
                return apiResult;
            }).thenCompose(apiResult ->
                    sessionHandler.createLocalSession(apiResult.getResult())
            ).thenApplyAsync((sessionId) -> {
                // 本地会话创建成功之后，将本地会话标识写入客户端cookie
                BrowserCookie cookie = new BrowserCookie(StandardCacheKeyBuilder.getAuthTokenKey(), sessionId, DurationConstant.HOUR_12.instance());
                ServletCookieScene.writeCookie(cookie);

                return sessionId;
            });
        };

        // 此处，根据登录账户和前端会话标识token直接调用方法sessionService.loginValidate()判断是否提前返回结果！
        return sessionHandler.loginValidate(loginQO.getAccount()
        ).thenComposeAsync(sid -> StringUtils.isEmpty(sid)
                ? loginAction.apply(loginQO)
                : BenchmarksFuture.completedFuture(sid)
        ).thenApplyAsync(sessionId -> ApiResult.<String>build()
                .setStatus(ApiStatusEnum.SUCCESS)
                .setResult(sessionId)
        ).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

    /**
     * 退出登录
     */
    public BenchmarksFuture<ApiResult<Boolean>> logout() {
        // 注销本地会话
        return sessionHandler.removeLocalSession(
        ).thenApplyAsync(flag -> {
            ApiResult<Boolean> apiResult = ApiResult.build();
            if (!flag) {
                return apiResult.setStatus(ApiStatusEnum.SUCCESS).setResult(false);
            }
            // 本地会话注销之后，清除cookie
            BrowserCookie cookie = new BrowserCookie(StandardCacheKeyBuilder.getAuthTokenKey(), StringUtils.EMPTY, Duration.ZERO);
            ServletCookieScene.writeCookie(cookie);
            return apiResult.setStatus(ApiStatusEnum.SUCCESS).setResult(true);
        }).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

    /**
     * 获取当前登录用户
     */
    public BenchmarksFuture<ApiResult<AuthLoginBO>> current() {
        return sessionHandler.readLocalSession(
        ).thenApplyAsync((AuthLoginBO currentUser) -> ApiResult.<AuthLoginBO>build()
                .setStatus(ApiStatusEnum.SUCCESS)
                .setResult(currentUser)
        ).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

}
